﻿using System;
using System.Collections.Generic;
using System.Text;

namespace MemoryGameBL
{
    public class Game
    {
        public enum eOpponentType
        {
            Human,
            Computer
        }

        private const bool k_FirstPlayerPlaying = true;

        private PlayerWithScore m_FirstPlayerWithScore;
        private PlayerWithScore m_SecondPlayerWithScore;
        private eOpponentType m_OpponentType = eOpponentType.Human;
        private eOpponentType m_CurrentPlayerType = eOpponentType.Human;
        private Player m_CurrentlyPlayingPlayer;
        private Board m_FullBoard;
        private Board m_PlayingBoard;
        private Board m_ComputerVisibleBoard;
        private bool m_FirstPlayerPlaying;
        private Cell m_FirstCell = null;
        private Cell m_SecondCell = null;

        public Game(string i_FirstPlayerName)
        {
            m_OpponentType = eOpponentType.Computer;
            this.m_FirstPlayerWithScore.Player = new Player(i_FirstPlayerName);
            this.m_SecondPlayerWithScore.Player = new Player("Computer");
        }

        public Game(string i_FirstPlayerName, string i_SecondPlayerName)
        {
            m_OpponentType = eOpponentType.Human;
            this.m_FirstPlayerWithScore.Player = new Player(i_FirstPlayerName);
            this.m_SecondPlayerWithScore.Player = new Player(i_SecondPlayerName);
        }

        /// <summary>
        /// Initializes a single round.
        /// </summary>
        /// <param name="i_BoardHeight">The height of the board.</param>
        /// <param name="i_BoardWidth">The weight of the board.</param>
        public void InitRound(int i_BoardHeight, int i_BoardWidth)
        {
            this.m_FullBoard = new Board(i_BoardHeight, i_BoardWidth);
            this.m_PlayingBoard = new Board(i_BoardHeight, i_BoardWidth);
            this.m_ComputerVisibleBoard = new Board(i_BoardHeight, i_BoardWidth);

            this.m_FullBoard.FillRandomPairCharacters();
            this.m_PlayingBoard.FillSpaces();

            this.m_FirstPlayerWithScore.Score = 0;
            this.m_SecondPlayerWithScore.Score = 0;

            this.m_FirstPlayerPlaying = k_FirstPlayerPlaying;
            this.m_CurrentlyPlayingPlayer = m_FirstPlayerWithScore.Player;
        }

        public PlayerWithScore FirstPlayerWithScore
        {
            get { return this.m_FirstPlayerWithScore; }
        }

        public PlayerWithScore SecondPlayerWithScore
        {
            get { return this.m_SecondPlayerWithScore; }
        }

        public bool IsGameEnded
        {
            get { return (this.m_FirstPlayerWithScore.Score * 2) + (this.m_SecondPlayerWithScore.Score * 2)  == (this.m_PlayingBoard.NumberOfColumns * this.m_PlayingBoard.NumberOfRows); }
        }

        /// <summary>
        /// Returns the player that won, if the game has already ended.
        /// </summary>
        public Player Winner
        {
            get
            {
                Player retVal = null;

                if (this.IsGameEnded)
                {
                    retVal = m_FirstPlayerWithScore.Score > m_SecondPlayerWithScore.Score ?
                        m_FirstPlayerWithScore.Player : m_SecondPlayerWithScore.Player;
                }

                return retVal;
            }
        }

        /// <summary>
        /// Returns the playing board. This board contains only the visible cells.
        /// </summary>
        public Board PlayingBoard
        {
            get { return this.m_PlayingBoard; }
        }

        /// <summary>
        /// Returns the player that goes now.
        /// </summary>
        public Player CurrentlyPlayingPlayer
        {
            get { return this.m_CurrentlyPlayingPlayer; }
        }

        /// <summary>
        /// Returns the type of the currently playing player.
        /// </summary>
        public eOpponentType CurrentPlayerType
        {
            get { return m_CurrentPlayerType; }
        }

        /// <summary>
        /// Shows a cell on the board.
        /// </summary>
        /// <param name="i_Cell">A cell to open.</param>
        /// <returns>The board containing the opened cell.</returns>
        public Board OpenCell(Cell i_Cell)
        {
            this.m_PlayingBoard[i_Cell] =
                this.m_FullBoard[i_Cell];

            if (this.m_FirstCell == null)
            {
                this.m_FirstCell = i_Cell;
            }
            else if (this.m_SecondCell == null)
            {
                this.m_SecondCell = i_Cell;
            }

            return this.m_PlayingBoard;
        }

        public Board GenerateAndGetComputerMove()
        {
            List<Cell> emptyCellsList = this.m_PlayingBoard.GetEmptyCells();

            Random random = new Random();
            int randCell = random.Next(emptyCellsList.Count);
            this.OpenCell(emptyCellsList[randCell]);

            emptyCellsList = this.m_PlayingBoard.GetEmptyCells();

            random = new Random();
            randCell = random.Next(emptyCellsList.Count);

            return this.OpenCell(emptyCellsList[randCell]);
        }

        public void DoNextTurn()
        {
            if (this.m_FirstPlayerPlaying)
            {
                if (this.m_FullBoard[m_FirstCell] == this.m_FullBoard[m_SecondCell])
                {
                    this.m_FirstPlayerWithScore.Score++;
                }
                else
                {
                    this.m_PlayingBoard[m_FirstCell] = ' ';
                    this.m_PlayingBoard[m_SecondCell] = ' ';
                    
                    this.m_FirstPlayerPlaying = !k_FirstPlayerPlaying;
                    m_CurrentlyPlayingPlayer = this.m_SecondPlayerWithScore.Player;
                    m_CurrentPlayerType = m_OpponentType;
                }

                this.m_FirstCell = null;
                this.m_SecondCell = null;
            }
            else
            {
                if (this.m_FullBoard[m_FirstCell] == this.m_FullBoard[m_SecondCell])
                {
                    this.m_SecondPlayerWithScore.Score++;
                }
                else
                {
                    this.m_PlayingBoard[m_FirstCell] = ' ';
                    this.m_PlayingBoard[m_SecondCell] = ' ';
                        
                    this.m_FirstPlayerPlaying = k_FirstPlayerPlaying;
                    m_CurrentlyPlayingPlayer = this.m_FirstPlayerWithScore.Player;
                    m_CurrentPlayerType = eOpponentType.Human;
                }

                this.m_FirstCell = null;
                this.m_SecondCell = null;
            }
        }
    }
}
